import math


"""

UserBasedCF是基于用户协同过滤算法，是用户对项目的历史偏好，发掘用户之间的相关性

步骤：

     1、找到和目标用户兴趣相似的用户集合

	 2、找到这个集合中的用户喜欢的，且目标用户没有的物品推荐给目标用户



"""


# 定义基于用户的协同过滤算法类



# 初始化对象


def UserSimilarity(train):
    # 建立物品-用户的倒排表

    # 数据格式：key:物品 value:用户1，用户2

    item_users = dict()

    # 遍历训练集中用户-物品数据

    for user, items in train.items():

        # 遍历用户对应的物品数据

        for i in items.keys():

            # 倒排表还没有该物品

            if i not in item_users:
                # 倒排表中该物品项赋值为set()集合

                item_users[i] = set()

            # 倒排表中该物品项添加该用户

            item_users[i].add(user)

    # 计算用户-用户相关性矩阵

    C = dict()  # 用户-用户共现矩阵(某用户,与其他用户出现交集的个数)

    N = dict()  # 用户产生行为的物品个数（不同用户的物品个数）

    # 遍历物品-用户的倒排表，取得物品-用户数据

    for i, users in item_users.items():

        # 遍历物品i下的用户

        for u in users:

            # 初始化用户产生行为的物品个数0

            N.setdefault(u, 0)

            # 遍历到该用户加1

            N[u] += 1

            # 用户-用户共现矩阵初始化

            C.setdefault(u, {})

            # 遍历该物品下所有的用户

            for v in users:

                # 若该项为当前用户，跳过

                if u == v:
                    continue

                # 遍历到其他不同用户则加1

                # 初始化为0

                C[u].setdefault(v, 0)

                # 加1

                C[u][v] += 1

    # 计算用户-用户相似度，余弦相似度

    W = dict()  # 相似度矩阵

    # 遍历用户-用户共现矩阵的所有项，

    # 每行用户、该行下的其他用户

    for u, related_users in C.items():

        # 存放用户间相似度

        W.setdefault(u, {})

        # 遍历其他每一个用户及对应的同现矩阵的值，即分子部分

        for v, cuv in related_users.items():
            # 余弦相似度

            W[u][v] = cuv / math.sqrt(N[u] * N[v])

        # 返回用户相似度

    return W


# 给用户user推荐，前K个相关用户喜欢的，

# 用户user未产生过行为的物品

# 默认3个用户，推荐10个物品

def Recommend(W, user, train, K=3, N=10):
    # 用户user对物品的偏好值

    rank = dict()

    # 用户user产生过行为的物品项item

    action_item = train[user].keys()

    # 对用户user按相似度从大到小进行排列

    # 取与用户user相似度最大的K个用户

    for v, wuv in sorted(W[user].items(), key=lambda x: x[1], reverse=True)[0:K]:

        # 遍历前K个与user最相关的用户

        # 遍历每件物品、用户对该物品的偏好值

        for i, rvi in train[v].items():

            # 若用户user对物品i已有评价，则跳过

            if i in action_item:
                continue

            # 计算用户user对物品i的偏好值

            # 初始化该值为0

            rank.setdefault(i, 0)

            # 通过与其相似用户对物品i的偏好值相乘并相加

            rank[i] += wuv * rvi

        # 按评分值大小，为用户user推荐结果的取前N个物品

    return dict(sorted(rank.items(), key=lambda x: x[1], reverse=True)[0:N])


from Core.Config import Config
config = Config()
database = config.DataBase()
useractions = database.find("Recommender", "UserAction_Plus", query = {"limit" : 10000})
train = {}
for useraction in useractions:
    user = useraction["userpin"]
    article = useraction["article_id"]
    score = useraction["type"]
    if score == "COLLECT":
        score = 1
    elif score == "LIKE":
        score = 1
    elif score == "DISLIKE":
        score = -1
    else:
        score = 0
    if user not in train:
        train[user] = {}
        train[user][article] = score
    else:
        train[user][article] = score



"""
UserSimilarity = UserSimilarity(train)
for user in UserSimilarity:
    for i in user:
        related_user = i
        related_para = user[related_user]
"""


